// 学习路线
    // 1) vue基础
    // 2) vue-cli(vue脚手架,专门用于工程化开发)
    // 3) vue-router(用于在vue当中实现前端路由)
    // 4) vuex
    // 5) element-ui
    // 6) vue3








// 1.vue基础
    // 1.vue是什么：用于构建用户界面的渐进式JavaScript框架。
        // 渐进式：Vue可以自底向上逐层应用。
        // 简单应用：只需要一个轻量小巧的核心库。
        // 复杂应用：可以引入各式各样的Vue插件。


    // 2.谁开发了Vue：尤雨溪(国产框架) 
        // Vue是后起之秀，生态完善，已然成为国内前端工程师必备技能
        // Vue发展历程
            // 2013年---受到Angular框架的启发，尤雨溪开发出了一款轻量级框架 Seed。同年12月，Seed更名为Vue，版本0.6.0 
            // 2014年---Vue正式对外发布，版本号0.8.0。Taylor otwell在Twitter上发表动态，说自己正在学习Vue.js。
            // 2015年---10月24日，正式发布Vue1.0.0 Evangelion (新时期福音战士)
            // 2016年---10月1日，正式发布Vue2.0.0 Ghost in the Shell(攻壳机动队)
            // 2020年---9月18日，正式发布Vue3.0.0 One Piece(海贼王)


    // 3.Vue特点
        // 1)采用组件化模式，提高代码复用率、且让代码更好维护。
        // 2)声明式子编码，让编码人员无需直接操作DOM，提高开发效率。
        // 3)使用虚拟DOM+优秀的Diff算法，尽量复用DOM节点。
    


    // 4.学习Vue之前要掌握的JavaScript基础知识？
        // ES6语法规范
        // ES6模块化
        // 包管理
        // 原型、原型链
        // 数组常用方法
        // axios
        // promise





    // 5.Vue第一个小案例
        // 1)阻止Vue在启动时生成生产提示
            Vue.config.productionTip = false;

        // 2)创建一个Vue对象并且把数据渲染到页面中
            // html代码(容器)
                <div id="root">
                    <h1>{{name}}</h1>
                    <h2>{{age}}</h2>
                    <h3>{{sex}}</h3>
                    <h4>{{subject}}</h4>
                </div>

            // js代码(vue实例)
                new Vue({
                    el : '#root', //告诉Vue它的容器id名
                    data : {      //data用于数据存储，数据供el容器使用
                        name : 'zhansan',
                        age : 18,
                        sex : 'male',
                        subject : 'javaScript'
                    }
                })

        // 总结
            //想让Vue工作，就必须创建一个Vue实例，且要传入一个配置对象。
            // root容器里的代码依旧负荷html规范，只不过混入了一些Vue语法。
            // root容器里的代码被称为[Vue模板] 。
            // Vue实例和容器是一一对应的。
            // 真实开发中只有一个Vue实例，且会配合着组件一起使用。
            // {{xxx}}中的xxx要写js表达式，且xxx可以自动读取到data中的所有制。
            // 一旦data中的数据发生改变，那么模板中用到该数据的地方也会自动更新。





    // 6.Vue模板语法
        // 1)插值语法
            /*
            <div class="root">
                你好,{{name}}
            </div>
            */
            new Vue({
                el: '.root',
                data: {
                    name: 'zhansan'
                }
            })
        // 2)指令语法
            // V-bind
                // 完整语法
                    // <a v-bind:href='数据名'></a>
                // 简写语法
                    // <a :href='数据名'></a>
                // 动态参数的缩写
                    // <a :[动态的参数值]='数据名'></a>


        // Vue模板语法总结(分两大类)：
            // 1)插值语法
                // 功能：用于解析标签体内容。
                // 写法：{{xxx}}， xxx是js表达式，且可以直接读取到data中的所有属性
            // 2)指令语法
                // 功能：用于解析标签(包括：标签属性、标签体内容、绑定事件······)
                // 举例：v-bind:href='xxx' 或 简写为 :href='xxx', xxx同样要写js表达式。且可以直接读取data中所有属性。
                // 备注：Vue中有很多指令，且形式都是：v-????，此处我们只是哪v-bing举例子。


    





    // 7.数据绑定
        // 单向数据绑定(v-bind)：数据只能从data流向页面
            // 容器代码
                /*
                <div id="app">
                    <input type="text" v-bind:value="data">
                </div>
                */
            // 实例代码
                new Vue({
                    el: '#app',
                    data: {
                        data: '单向数据绑定'
                    }
                })

        // 双向数据绑定(v-model)：数据不仅能从data流向页面，也可以从页面流向data。
            // 注意
            // 1.双向绑定应用在表单类元素上(如：input、select等)
            // 2.v-model:value 可以简写为v-model 因为v-model默认收集的就是value值。


            // 容器代码
                /*
                <div id="app">
                    <input type="text" v-model="data">
                </div>
                */
            // 实例代码
            new Vue({
                el: '#app',
                data: {
                    data: '双向数据绑定'
                }
            })
        

        
            

    

    // 8.el和data的两种写法
        // el的挂载方法
            new Vue({
                el: '#app' // 第一种写法
            })

            const vm = new Vue()
            vm.$mount('#app');  //第二种方法

        // data的第二种写法必须写成一个函数，并且return一个对象出去
            new Vue({
                el: '#app',
                data: {
                    url: 'https://www.baidu.com' //第一种写法
                }
            })


            new Vue({
                el: '#app',
                data() {
                    return {
                        url: 'https://www.baidu.com' //第二种写法(建议使用这种方法)
                    }
                }
            })
    



    // 9.理解MVVM模型
        // 1) M: 模型(model)    对应data中的数据
        // 2) V: 视图(view)     模板(在Vue里面指的就是页面)
        // 3) VM:视图模型(ViewModel) Vue实例(new Vue)

        // 总结：
            // data中所有的属性，最后都出现在了vm身上
            // vm身上的所有属性 以 Vue原型上所有属性，在Vue模板中都可以使用。




    





    // 10.数据代理
        // 通过一个对象代理对另外一个对象中属性的操作(操作包括读和写)
            // 最简单的数据代理，通过obj2的x属性可以访问obj1的x属性，并且通过obj2的x属性可以修改obj1的x属性
                const obj = {
                    x: 100
                }
                const obj1 = {
                    name : 'zhansan',
                    age: 18
                }

                Object.defineProperty(obj1, 'x', {
                    get() {
                        return obj.x;
                    },
                    set(value) {
                        obj.x = value;
                    }
                })

        
        // 总结：
            // 1.Vue中的数据代理
                // 通过vm对象来代理data对象中属性的操作(读写)
            // 2.Vue中数据代理的好处
                // 更加方便的操作data中的数据
            // 3.基本原理
                // 通过Object.defineProperty()把data对象中所有属性添加到vm上。
                // 为每一个添加到vm上的属性，都指定一个getter/setter。
                // 在getter/setter内部去操作(读/写)data中对应的属性。


    





    // 11.事件
        // v-on:click
        // 例如：
            // 模板代码
                // <button v-on:click='hello'>点我弹窗(不传参)</button>
            // 简写方法
                // <button @click='hello($event, 666)'>点我弹窗(传参)</button>
                
            // 实例代码
                const vm = new Vue({
                    data: {

                    },
                    methods: {   //方法建议都写到methods里面
                        hello() {
                            // this指向vue实例
                            // 如果要获取自身请使用 event.target
                        },
                        hello1(event, number) {
                            console.log(event.target);
                            console.log(number);
                        }
                    }
                })
            

        // 事件基本使用总结：
            // 1) 使用v-on:xxx 或 @xxx 绑定事件，其中xxx是事件名
            // 2) 事件的回调需要配置在methods对象中，最终会在vm上
            // 3) methods中配置的函数，不要用箭头函数！否在this就不是vm了
            // 4) methods中配置的函数，都是被Vue索管理的函数，this的指向是vm 或 组件实例对象
            // 5) @click="demo" 和 @click="demo($event)" 效果一致，但后者可以传参

        


        // 可能会用到的事件
            // @scroll 滚动条滚动的事件
            // @wheel  鼠标滚轮滚动的事件











    // 12.事件修饰符
        // Vue的事件修饰符：
            // 1) prevent 阻止默认事件(常用)
            // 2) stop 阻止事件冒泡(常用)
            // 3) once 事件只被触发一次
            // 4) capture 使用事件的捕获模式(事件的处理是在冒泡的时候处理的，用了这个属性就相当于事件在捕获的时候就处理了)
            // 5) self 只有event.target是当前操作的元素时被触发(只有当点击自己本身的时候才能触发，无法通过冒泡触发)
            // 6) passive 事件的默认行为立即执行，无需等待事件回调执行完毕
            // 7) native 给组件标签绑定原生事件的时候需要加上native，不然组件标签会把该事件当成自定义事件


        // 例如：
            // 模板代码
                // <a
                //     @click.prevent='cli'   //清除a标签的默认点击事件(事件修饰符语法)
                // </a>


            // 实例代码
                const vm = new Vue({
                    data() {
                        return {

                        }
                    },
                    methods: {
                        cli() {
                            console.log('阻止a标签的默认点击事件')
                        }
                    }
                }).$mount('#app');






    


    // 13.键盘按键
        // @keyDown  键盘按下
        // @keyUp    键盘弹起

        // Vue给常用的按键设置了别名：
            // 1) enter => 回车
            // 2) delete => 删除和退格
            // 3) esc => 退出
            // 4) space => 空格
            // 5) tab => 换行(只能用在keydown上)
            // 6) up => 上
            // 7) down => 下
            // 8) left => 左
            // 9) right => 右

            // 系统修饰键(用法特殊)：ctrl   shift  alt  meta
                // 配合keyup使用：案件下修饰键的同时，在按下其他键，随后释放其他键，事件才能被触发。
                // 配合keydown使用：正常触发
                    // 10) ctrl => 控制
                    // 11) alt => 交替键
                    // 12) shift => 上档
                    // 13) meta => win键


            // Vue未提供的别名的按键，可以使用按键原始的key值去绑定，但注意要转为kebab-case(端横线命名)  例如：@keyup.caps-lock这个是大小写键
            // 也可以使用keyCode去指定具体的按键(不推荐)    例如：@keyup.13 绑定回车键
            // Vue.config.keyCodes.自定义键名 = 键码，可以去定制按键别名
                // 例如：
                    Vue.config.keyCodes.huiche = 13  //自定义一个回车键

            

            // 注意：事件修饰符可以连续调用
                // @click.once.prevent='demo($event)'  
        // 例如：
            // 模板代码
                // <input type="text" @keyDown.enter='demo($event)'>  //给keyDown添加一个事件修饰符 enter
        


        // 小案例：当我按下ctrl+y的时候console.log(123)
            // <input type="text" @keyup.ctrl.y='demo($event)'>
            const vm = new Vue({
                el: '#app',
                data: {

                },
                methods: {
                    demo(event) {
                        console.log(123)
                    }
                }
            })
            










    // 14.计算属性
        // 总结：
            // 1.定义：要用的属性不存在，要通过已有属性计算得来。
            // 2.原理：底层借助Object.defineProperty方法提供的getter和setter。
            // 3.get函数什么时候执行？
                // 1) 初次地区是会执行一次
                // 2) 当依赖的数据发生改变时再次被调用
            // 4.优势：于methods实现相比，内部有缓存机制(复用)，效率更好，调试方便。
            // 备注：
                // 1) 计算属性最终会出现在vm上，直接读取使用即可。
                // 2) 计算属性要被修改，那必须写set函数去响应修改，且set中要引起计算时依赖的数据发生改变。


        // 代码
        const vm = new Vue({
            el: '#app',
            data: {
                firstName: 'zhang',
                lastName: 'san'
            },
            methods: {

            },
            computed: {   //computed(计算属性的内容都放到这里面)
                fullName: {   //需要把计算属性写成一个对象
                    // get有什么用？当fullName被人读取的时候执行get且返回值就作为fullName的值
                    // get什么时候被调用？1.初次调用fullName的时候。  2.所依赖的数据发生变化时。
                    get() {   
                        // 对象里面有个get方法(get方法里面的this指向vm，Vue已经帮我们把this指向调好了)
                        
                        //get方法里面必须有一个返回值
                        return this.firstName + '-' + this.lastName;
                    },

                    // set什么时候调用？当fullName被修改的时调用
                    set(value) {
                        let arr = value.split('-');
                        this.firstname = arr[0];
                        this.lastname = arr[1];
                    }
                }
            }
        })



        // 计算属性简写(注意：只有读取没有修改的时候才能用简写形式)
        const vm = new Vue({
            el: '#app',
            data: {
                firstName: 'zhang',
                lastName: 'san'
            },
            computed: {
                // 如果你的计算属性里面没有set，可以简写为以下方式，可以不用谢get了，下面这样就相当于把代码写到了get里面
                fullName() {
                    return this.firstName + '-' + this.lastName;
                }
            }
        })


    // 15.侦听器
        // 监视属性watch总结：
            // 1.当监视属性变化时，回调函数自动调用，进行相关操作
            // 2.监视的属性必须存在，才能进行监视
            // 3.监视的两种写法：
                // (1) new Vue时传入一个watch配置
                // (2) 通过vm.$watch监视



        // watch 侦听器
        const vm = new Vue({
            data : {
                tag : true
            },
            watch : {
                tag : {  //监听data对象里面的tag属性，只要tag发生变化就会触发handler执行
                    immediate : true,  //初始化的时候让handler调用以下

                    handler(newValue, oroginValue) {   //tag里面的方法必须写handler，handler里面有两个参数，第一个参数是监听属性新的值，第二个参数是监听属性的原始值
                        console.log('tag发生变化了');
                        console.log(newValue, oroginValue);   
                    }
                }
            }
        }).$mount('#app');


        // watch 侦听器的另外一种写法
            // vm实例中的$watch方法传两个参数，第一个是需要监视目标的属性名，第二个是配置对象，对象里面的写法和上面相同
        vm.$watch('ishot', {
            handler(newValue, originValue) {
                console.log('数据发生了变化', 'newValue : ' + newValue, 'originValue : ' + originValue)
            }
        })






        
        // 深度监视
            // 深度监视总结：
                // (1) Vue中的watch默认不监测对象内部值的改变(一层)。
                // (2) 配置deep : true 可以检测对象内部值改变(多层)。
            // 备注：
                // (1) Vue自身可以检测对象内部值的改变，但是Vue提供的watch默认不可以
                // (2) 使用watch时根据数据的具体结构，决定是否采用深度监视。
            const vm = new Vue({
                data : {
                    numbers : {
                        a : 1,
                        b : 2
                    }
                },
                watch : {
                    'numbers.a' : {  //单独监视numbers对象里面的a属性
                        handler() {
                            console.log('number.a发生变化了')
                        }
                    },
                    numbers : {
                        deep : true,  //深度监视，监视整个number对象，当对象中的任何一个属性值发生改变的时候都会执行handler
                        handler() {
                            console.log('number里面的数据发生改变了')
                        }
                    }
                }
            }).$mount('#app');



        // 深度监视简写模式
            const vm = new Vue({
                watch : {
                    ishot() {
                        // 当你不需要配置 immediate 和 dee ，且只有handler的时候就可以使用简写模式
                        
                    }
                }
            }).$mount('#app')
        
        // 另一种写法的简写模式
        vm.$watch('ishot', function() {
            console.log('ishot发生了变化')
        })
            

        
    

    // computed 和watch之间的区别：
        // (1): computed能完成的功能，watch都可以完成。
        // (2): watch 能完成的功能，computed不一定能完成，例如：watch可以进行异步操作。
    // 两个重要的小原则：
        // 1.所有Vue管理的函数，最好写成普通函数，这样this的指向才是vm或组件实例对象。
        // 2.所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等)，最好写成箭头函数。
        //   这样this的指向才是vm或组件实例对象。




        













    // 16. 绑定class样式
        // class样式总结：
            // 写法:class="xxx" xxx可以是字符串、对象、数组。
            // 字符串写法适用于：类名不确定，要动态获取
            // 对象写法适用于：要绑定多个样式，个数不确定，名字也不确定
            // 数组写法适用于：要绑定多个样式，个数确定，名字也确定，但不确定用不用。

        
        // 1) 绑定class样式---字符串写法，适用于：样式的类名不确定，需要动态指定
            // 模板代码
                // <div id="app">
                                        //把需要动态改变的class绑定起来
                //     <div class="basic" :class='property' @click='changMood'>{{name}}</div>
                // </div>

            // 实例代码
                const vm = new Vue({
                    data : {
                        name : '惠州城市学院',
                        property : ''
                    },
                    methods : {
                        changMood() {
                            this.property = 'normal'
                        }
                    }
                }).$mount('#app');


        // 2) 绑定class样式---数组写法，适用于：要绑定的样式个数不确定，名字也不确定。
            // 模板代码
                // <div id="app">
                                                //写数组
                //     <div class="basic" :class='compoundStyle'>{{name}}</div>
                // </div>
            // 实例代码
                const vm = new Vue({
                    data : {
                        name : '惠州城市学院',
                        compoundStyle : ['option1', 'option2', 'option3']  //后期如果需要动态修改，直接修改数组里面的值就可以了
                    }
                }).$mount('#app');




        // 3) 绑定class样式--对象写法，适用于：要绑定的样式个数确定、名字也确定、但要动态决定用不用
            // 模板代码
                // <div id="app">
                //     <div class="basic" :class="classObj">{{name}}</div>
                //  </div>
            // 实例代码
                const vm = new Vue({
                    data : {
                        name : '惠州城市学院',
                        property : '',
                        classObj : {
                            option1 : true,  //为true代表使用该样式
                            option2 : true,
                            option3 : false,  //为false代表不适用该样式
                        }
                    }
                }).$mount('#app');





    // 17.内联样式绑定
        // style样式总结：
                // :style="{fontSize : xxx}" 其中xxx是动态值
                // :style="[a, b]" 其中a、b是样式对象


        // 1) 字符串写法
            // 模板代码
                // <div id="app">
                //     <div :style="{fontSize: 50 + 'px', color : fcolor}">{{name}}</div>
                // </div>
            // 实例代码
                const vm = new Vue({
                    data : {
                        name : '惠州城市职业技术学院',
                        fsize : 50,
                        fcolor : 'red'
                    }
                }).$mount('#app');




        // 2) 对象写法
            // 模板代码
                // <div id="app">
                //     <div :style="styleObj">{{name}}</div>
                // </div>

            // 实例代码
                const vm = new Vue({
                    data : {
                        name : '惠州城市职业学院',
                        styleObj : {
                            fontSize: '50px',
                            color : 'red'
                        }
                    }
                }).$mount('#app');



        // 数组写法
            // 模板代码
                // <div id="app">
                //     <div :style='obj'>{{name}}</div>
                // </div>
            // 实例代码
                const vm = new Vue({
                    data : {
                        name :'惠州城市学院',
                        obj : {
                            fontSize : '50px',
                            color : 'red',
                            backgroundColor : '#ccc',
                            
                        },
                        obj1 : {
                            width : '400px',
                            height : '200px',
                            textAlign : 'center',
                            lineHeight : '200px'
                        },
                        arr : [this.obj, this.obj1]
                    }
                }).$mount('#app');











    // 18.条件渲染
        // 总结：
            // 1.v-if
                // 写法：
                    // (1) v-if="表达式"
                    // (2) v-else-if="表达式"
                    // (3) v-else="表达式"
                // 适用于:切换频率第地场景。
                // 特点：不展示的dom元素直接被移除
                // 注意：v-if可以和v-else-if、v-else一起使用，但要求结构不能被打断。

            // 2.v-show
                // 写法：v-show="表达式"
                // 适用于：切换频率较高的场景。
                // 特点：不展示DOM元素未被移除，仅仅是使用样式隐藏

            // 备注：使用v-if是，元素可能无法获取到，而使用v-show一定可以获取到


        // 模板代码
        // <div id="app">
            // 当v-show里面的条件满足的时候这个div就会display=block否则就display=none
            // <div v-show='n === 1'>Angular</div> 

            // v-if  v-else-if v-else  如果不满足条件整个dom元素都会删除，如果满足条件又会重新创建出来
            // <div v-if="type === 'A'">
            //     A
            // </div>
            // <div v-else-if="type === 'B'">
            //     B
            // </div>
            // <div v-else-if="type === 'C'">
            //     C
            // </div>
            // <div v-else>
            //     Not A/B/C
            // </div>
        // </div>
        

        // 注意：
            // <div v-if="1 === 1">{{1}}</div>
            // <div v-if="1 === 1">{{1}}</div>
            // <div v-if="1 === 1">{{1}}</div>

            // 如果条件相同请写成下面这种形式(因为template不会被当做dom结构，它知识一个模板)
            
            // <template v-if='1 === 1'>
            //     <div>{{1}}</div>
            //     <div>{{1}}</div>
            //     <div>{{1}}</div>
            // </template>









    // 19.列表渲染
        // v-for指令总结：
            // 1.用于展示列表数据
            // 2.语法：v-for="(item, index) in xxx" :key="yyy"
            // 3.可遍历：数组、对象、字符串(用得很少)、指定次数(用得很少)

        // 遍历数组(常用)
            // 模板代码
                // <ul>
                                  //值    //索引
                //     <li  v-for="(value, index) in persons" :key='value.id'>   //使用v-for遍历persons数组   后面的:key="value.id"给li标签打上一个唯一标识
                //         <span>{{value.name}}</span>
                //         <span>{{value.age}}</span>
                //     </li>
                // </ul>
            
            // 实例代码
                const vm = new Vue({
                    data : {
                        persons : [
                            {id: 001, name : '张三', age : 18},
                            {id: 002, name : '李四', age : 20},
                            {id: 003, name : '王五', age : 19},
                            {id: 004, name : '诸葛', age : 21}
                        ]
                    }
                }).$mount('#app');





        // 遍历对象(比较常用)
            // 模板代码
                              //值   //键  //索引      //给每个id设置一个key(必须是独一无二的)
                // <li v-for="(value, key, index) in car" :key="index">
                //     {{key}}-{{value}}-{{index}}
                // </li>
            // 实例代码
                const vm = new Vue({
                    data : {
                        car : {
                            name : 'BMW',
                            color : 'red',
                            price : '15000$',
                            width : '5000px',
                            height : '1000px'
                        }
                    }
                }).$mount('#app');



        // 遍历字符串(用得少)
            // 模板代码
                              //值    //索引
                // <li v-for="(value, index) in str" :key="index">
                //     {{value}} - {{index}}
                // </li>  

        // 遍历指定次数(用的少)
            // 模板代码
                      //值(从1开始到11) //索引(从0开始到9)   //总共遍历十次
            // <li v-for="(value, index) in 10" :key="index">
            //     {{value}} - {{index}}
            // </li>

        





        // key的作用和原理详解(很重要)：
            // 面试题：react、vue中的key有什么作用？(key内部原理)

                // 1.虚拟DOM中key的作用；
                    // key是虚拟DOM对象的标识，当状态中的数据发生变化时，Vue会根据[新数据] 生成 新的虚拟DOM
                    // 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较，比较规则如下：


                    // 2.对比规则：
                    // 1)：旧虚拟DOM中找到于新虚拟DOM相同的key：
                        // ①. 若虚拟DOM种内容没变，直接使用之前的真是DOM
                        // ②. 若虚拟DOM中的内容变了，则生成新的真是DOM，随后替换掉页面中之前的真是DOM
                        
                    // 2)：旧虚拟DOM中未找到与新虚拟DOM相同的Key
                        // 创建新的真实DOM，随后渲染到页面


                    // 3.用index作为key可能发生的问题：
                        // 1.若对数据进行：逆序添加、逆序删除等破坏顺序的操作：
                            // 会产生没有必要的真是DOM更新 ===> 界面没有问题，但是效率低。

                        // 2.如果结构中还包括输入类的DOM：
                            // 会产生错误DOM更新 ==> 界面有问题

                    
                    // 4.开发中如果选择key?
                        // 1.最后使用每条数据的唯一表示作为key，比如id、手机号、身份证、学号等唯一值。
                        // 2.如果不存在对数据逆序添加、逆序删除等破坏顺序操作，仅用于渲染列表用于展示，使用index作为key是没有问题的。











    // Vue监视数据的原理：
        // 1.vue会监视data中所有层次的数据

        // 2.如何监测对象中的数据？
            // 通过setter实现数据监测，且要在new Vue时就传入要监测的数据。
                // (1) 对象中后追加的属性，Vue默认不做数据响应式处理
                // (2) 如果给后添加的属性做响应式，请使用如下API：
                    // Vue.set(target, propertyName/index, value) 或
                    // vm.$set(target, propertyName/index, value)
        

        // 3.如何监测数组中的数据？
            // 通过包裹数组更新元素的方法实现，本质就是做了两件事：
                // (1) 调用原生对应的方法对数组进行更新。
                // (2) 重新解析模板，进而更新页面。
        
        // 4.在Vue修改数组中的某个元素一定要用如下方法：
                // 1.使用这些API：push() 、pop()、shift()、unshift()、splice()、sort()、reverse()
                // 2.Vue.set() 或 vm.$set()

        // 特别注意：Vue.set() 和 vm.$set() 不能给vm或vm的根数据对象(根数据对象：向data，methods这些)添加属性 ！！！




        // 练习
            // 模块代码
                /*
                <div id="app">
                    <h1>学生信息</h1>
                    <button @click='student.age++'>年龄+1</button><br>
                    <button @click='addSex'>添加性别属性：默认是男</button><br>
                    <button @click.once='addfriendes'>在列表首位添加一个朋友</button><br>
                    <button @click='modifyName'>修改第一个朋友的名字为：张三</button><br>
                    <button @click.once='addHobby'>添加一个爱好</button><br>
                    <button @click="modifyHobby">修改第一个爱好为：开车</button><br>

                    <h3>姓名：{{student.name}}</h3>
                    <h3>年龄：{{student.age}}</h3>
                    <h3 v-if="student.sex">性别：{{student.sex}}</h3>
                    <h3>爱好</h3>
                    <ul>
                        <li v-for="(val, index) in student.hobby" :key='index'>
                            {{val}}
                        </li>
                    </ul>
                    <h3>朋友们</h3>
                    <ul>
                        <li v-for="(val, index) in student.friends" :key='index'>
                            {{val.name}}-{{val.age}}
                        </li>
                    </ul>
                </div>
                */

            // 实例代码
                const vm = new Vue({
                    data : {
                        student : {
                            name : 'zhansan',
                            age : 19,
                            hobby : ['抽烟', '喝酒', '烫头'],
                            friends : [
                                {name : 'lisi', age : 20},
                                {name : 'wangwu', age : 21}
                            ]
                        }
                    },
                    methods: {
                        addSex(event, def='男') {
                            this.$set(this.student, 'sex', def);
                        },
                        addfriendes() {
                            this.student.friends.unshift({name : '关羽', age : 50})
                        },
                        modifyName() {
                            this.student.friends[0].name = '张三'
                        },
                        addHobby() {
                            this.student.hobby.push('学习');
                        },
                        modifyHobby() { 
                            this.student.hobby.splice(0, 1, '开车');
                        }
                    }
                }).$mount('#app');







    // 20.收集表单数据
        // 总结：
            // 若：<input type='text'/>,则v-model收集的是value值，用户输入的就是value值。
            // 若：<input type='radio'/>,则v-model收集的是value值，且要给标签配置value值。
            // 若：<input type='checkbox'/>
                // 1.没有配置input的value属性，那么收集的就是checkbox(勾选 或 未勾选，是布尔值false or true)
                // 2.配置input的value属性：
                    // (1) v-model的初始值是非数组，那么收集的就是checked(勾选 或 未勾选，是布尔值false or true)
                    // (2) v-model的初始值是数组，那么收集的就是value组成的数组

            // 备注：v-model的三个修饰符
                // lazy：失去焦点在收集数据
                // number：输入字符串转为数字
                // trim：输入首尾空格过滤


        // 练习
            // 模板代码
            /*
            <div id="app">
                <h1>收集表单数据</h1>

                <!-- 账号密码 -->
                <form>
                    <div>
                        账号：<input type="number" v-model.number='userInfo.accountNumber' placeholder="请输入用户名">
                        <br>
                        <br>
                        密码：<input type.trim="password" placeholder="请输入密码" v-model='userInfo.password'>
                    </div>

                    <!-- 单选框 -->
                    <br>
                    性别：
                    男<input v-model='userInfo.radio' type="radio" name="sex" value="男"> &nbsp;&nbsp;&nbsp;
                    女<input v-model='userInfo.radio' type="radio" name="sex" value="女">

                    <!-- 复选框 -->
                    <br>
                    <br>
                    爱好：
                    敲代码<input v-model="userInfo.checkbox" value="敲代码" type="checkbox" name='hobby'> &nbsp;&nbsp;&nbsp;
                    开车<input v-model="userInfo.checkbox" value="开车" type="checkbox" name='hobby'> &nbsp;&nbsp;&nbsp;
                    学习<input v-model="userInfo.checkbox" value="学习" type="checkbox" name='hobby'>

                    <!-- 下拉菜单 -->
                    <br>
                    <br>
                    校区：
                    <select v-model="userInfo.select">
                        <option>请选择校区</option>
                        <option>广州</option>
                        <option>深圳</option>
                        <option>上海</option>
                        <option>北京</option>
                        <option>杭州</option>
                    </select>

                    <!-- 多行文本 -->
                    <br>
                    <br>
                    其他信息：<textarea v-model.lazy='userInfo.textarea' name="" id="" cols="30" rows="10"></textarea>

                    <!-- 确定框 -->
                    <br>
                    <br>
                    <input v-model='userInfo.confirm' type="checkbox" value="true">阅读并接受<a href="https://www.baidu.com">《用户协议》</a>
                    <br>
                    <br>
                    <input type="submit" @click.prevent='demo'>
                </form>
            </div>
            */


        // 实例代码
            const vm = new Vue({
                data : {
                    userInfo : {
                        accountNumber : '',
                        password : '',
                        radio : '男',
                        checkbox : [],
                        select : '请选择校区',
                        textarea : '',
                        confirm : ''
                    }
                },
                methods: {
                    demo(e) {
                        console.log(
                            // 点击提交的时候在控制台打印出所有收集的信息
                            `
                            用户名：${this.userInfo.accountNumber}
                            密码：${this.userInfo.password}
                            性别：${this.userInfo.radio}
                            爱好：${this.userInfo.checkbox}
                            校区：${this.userInfo.select}
                            其他信息：${this.userInfo.textarea}
                            是否接受协议：${this.userInfo.confirm}
                        `)
        
                        // 如果要把信息通过ajax发送给后台请使用这个方法
                        console.log(JSON.stringify(this.userInfo))
                    }
                },
            }).$mount('#app');



















    // 21.过滤器(filters)
        // 总结
            // 定义：对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)
            // 语法：
                // 1.注册过滤器：Vue.filter(name, callback) 或 new Vue{filters: {}}
                // 2.使用过滤器：{{xxx | 过滤器名}} 或 v-bind:属性="xxx | 过滤器名"
            // 备注：
                // 1.过滤器也可以接收额外参数、多个过滤器也可以串联
                // 2.并没有改变原本的数据，是产生新的对应数据


        // 局部过滤器
            // 模板代码
                // <div id="app">
                //     <h1>局部过滤器</h1>
                //     <div>{{str | myslice}}</div>
                // </div>

            // 实例代码
                let vm = new Vue({
                    data() {
                        return {
                            str : '局部过滤器的学习'
                        }
                    },
                    filters : {
                        // value的值就是管道前面的变量,
                        myslice(value) {
                            return value.slice(2, 4);  //过滤出(过滤)两个字
                        }
                    }
                }).$mount('#app');



        // 全局过滤器 
            // 所有的vue实例都可以调用，前提是得先创建过滤器然后在新建实例的情况下。
            // 模板代码
            /*
                <div id="app">
                    <h1>局部过滤器</h1>
                    <div>{{str | myslice}}</div>
                    <div :x='message | myslice'>惠城院</div>
                </div>
                <div id="root">
                    <h1>全局过滤器</h1>
                    <div>{{str | myslice}}</div>
                </div>
            */
        //    实例代码
            // 全局过滤器的创建方法
            Vue.filter('myslice', function(value) {
                return value.slice(2, 4);
            })
        
        
            let vm = new Vue({
                data() {
                    return {
                        str : '局部过滤器的学习',
                        message : '惠州城市学院'
                    }
                }
            }).$mount('#app');
        
            let vm1 = new Vue({
                data() {
                    return {
                        str : '全局过滤器的学习'
                    }
                }
            }).$mount('#root')







    // 22.内置指令
        // 学过的指令
            // v-bing : 单向绑定解析表达式，可简写为：xxx
            // v-model：双向数据绑定
            // v-for：遍历数组、对象、字符串
            // v-on：绑定事件监听，可简写为@
            // v-if：条件渲染(动态控制节点是否存在)
            // v-else-if：条件渲染(动态控制节点是否存在)
            // v-else：条件渲染(动态控制节点是否存在)
            // v-show：条件渲染(动态控制节点是否展示)
            

        // v-text指令：
            // 1.作用：向其所在的节点中渲染文本内容
            // 2.于插值语法的区别：v-text会替换掉节点中的内容，{{xxx}}则不会。

        // v-html指令：
            // 1.作用：向指定节点中渲染包含html结构的内容。
            // 2.于插值语法的区别：
                // (1) v-html会替换节点中的所有内容，{{xxx不会}}
                // (2) v-html可以识别html结构。
            // 3.严重注意：v-html会有安全性问题！！！
                // (1) 在网站上动态渲染任何html是非常危险的，容易导致xss攻击
                // (2) 一定要在可以信的内容上使用v-html，永远不要在用户提交的内容上 ！


        // v-cloak指令(没有值)
            // 1.本质是一个特殊属性，Vue实例创建完毕并接管容器后，会删掉v-cloak属性。
            // 2.使用css配置v-cloak可以解决网速慢时页面展现出{{xxx}}的问题。

        // v-once (没有值)
            // 1.v-once所在的节点在初次动态渲染之后，就视为静态内容了。
            // 2.以后数据的改变不会引起v-once所在结构的更新，就可以用于优化性能。 

        // v-pre (没有值)
            // 1.跳过其所在的节点编译过程。
            // 2.可利用它跳过：没有使用指令语法、没有使用插值语法的节点，会加快编译。







    // 23.自定义指令
        // 自定义指令总结：
            // 一、定义语法：
                // (1) 局部指令
                    new Vue({
                        directives : {
                            // 简写形式
                            fbind(element, binding) {

                            },
                            // 完整写法
                            abind : {
                                bind(element, binding) {

                                },
                                inserted(element, binding) {

                                },
                                update(element, binding) {

                                }
                            }
                        }
                    })
                // 全局指令
                    // 简写形式
                    Vue.directive('fbind', function(element, binding) {

                    })
                    // 完整写法
                    Vue.directive('fbind', {
                        bind(element, binding) {

                        },
                        inserted(element, binding) {

                        },
                        update(element, binding) {

                        }
                    })
            
            // 二、配置对象中常用的3个回调：
                    // (1) bind：指令于元素成功绑定时调用
                    // (2) inserted : 指令所在元素被插入页面时调用。
                    // (3) update：指令所在模板结构被重新解析时调用。

            // 三、备注：
                    // 1.指令定义时不加v-，但是用时一定要加v-
                    // 2.指令名如果是多个单词组成，要使用kebab-case命令方法，不要使用camelCase命名。

        // 局部指令
            // 需求1：定义一个v-big指令，和v-text功能类似，但会把绑定的数值放大十倍。
            // 简写形式：简写其实就是写了bind和update这两个方法，没有写inserted
                // 模板代码 
                    // <div id="app">
                        // <div v-big='num'></div>
                        // <button @click='num++'>点击num++</button>
                    // </div>
                
                // 实例代码
                    const vm = new Vue({
                        data : {
                            num : 1
                        },
                        directives : {
                            // big函数何时会被调用？1.指令于元素成功绑定时(一上来)。2.指令所在的模板被重新解析时
                            big(elemenet, binding) {
                                elemenet.innerText = binding.value * 10;
                            }
                        }
                    }).$mount('#app');

            
            // 需求2：定义一个v-fbind指令，和v-bind功能类似，但可以让其所绑定的input元素默认获取焦点。
                // 模板代码：
                    // <div id="app">
                    //     <input type="text" v-fbind:value='n'>
                    //     <button @click='n++'>点我n+1</button>
                    // </div>

                // 实例代码：
                    const vm = new Vue({
                        data : {
                            n : 1,
                            message : 'center'
                        },
                        directives : {
                            // big(elemenet, binding) {
                            //     console.log('test');
                            //     elemenet.innerText = binding.value * 10;
                            // }
                            fbind : {
                                // 当指令于元素成功绑定时(一上来)调用
                                bind(element, binding) {
                                    element.value = binding.value;
                                },
                                // 指令所在元素被插入页面时调用
                                inserted(element, binding) {
                                    element.focus();
                                },
                                // 指令所在的模板被重新解析时调用
                                update(element, binding) {
                                    element.value = binding.value;
                                }
                            }
                        }
                    }).$mount('#app');


        // 全局指令写法
            // 完整写法
                Vue.directive('fbind', {
                    bind(element, binding) {
                        element.value = binding.value;
                    },
                    inserted(element, binding) {
                        element.focus();
                    },
                    update(element, binding) {
                        element.value = binding.value;
                    }
                })


            // 完整写法
                // 简写形式
                Vue.directive('fbind', function(elemenet, binding) {
                    console.log(element, binding);
                })
    








    // 24.Vue生命周期
        const vm = new Vue({
            // 模板，Vue会自动读实例里面的template模板，和在页面上的写法相同
                // 注意：template模板里面只能有一个根元素，并且会替换掉整个容器，根标签不能使用<template></template>
            // template: `
            //     <div>
            //         <h1 :style="{opacity}">{{message}}</h1>
            //         <h3>{{message}}</h3>
            //     </div>
            // `,

            // 1.初始化：生命周期、事件、但数据代理还未开始
                // 此时：无法通过vm访问到data中的数据、methods中的方法。
            beforeCreate() {
                console.log('beforeCreate')
                // console.log(this)
            },

            // 2.初始化：数据监测、数据代理。
                // 此时：可以通过vm访问到data中的数据、methods中配置的方法。
            created() {
                console.log('created');
            },
            
            // 3.页面呈现的是未经编译的DOM结构。
                // 所有对DOM的操作，最终都不奏效。
            beforeMount() {
                // 不要在这里面操作DOM，最终都不会奏效的(操作了也无效)
                console.log('beforeMount');
            },

            // 4.页面呈现的是Vue经过编译的DOM。
                // 对DOM的操作均有效(尽可能避免)。至此初始化过程结束。
                // 一般在此进行：开启定时器、发送网络请求、订阅信息、绑定自定义事件、等初始化操作。
            mounted() {
                console.log('mounted');
            },

            // 5.此时：数据是新的，但页面时旧的。
                // 即：页面尚未和数据保持同步。
            beforeUpdate() {
                console.log('beforeUpdate');
            },

            // 6.此时：数据是新的，页面也是新的。
                // 即：页面和数据保持同步。
            updated() {
                console.log('update');
                // debugger;
            },

            // 7.此时vm中的：data、methods、指令等等，都处于可用状态，马上要执行销毁过程。
                // 一般在此过程：关闭定时器、取消订阅消息、解绑自定义事件等收尾操作。
            beforeDestroy() {
                console.log('beforeDestroy')
            },

            // 8.销毁完成
            destroyed() {
                console.log('destroyed')
            },
        })
        


        // vm的一生(vm的生命周期)：
                  // 将要创建 ===> 调用beforeCreate函数
                  // 创建完毕 ===> 调用create函数
                  // 将要挂载 ===> 调用beforeMount函数
            (重要)// 挂载完毕 ===> 调用mounted函数 。 ======>【重要的钩子】 总结：初始化的事情在这个钩子里面做
                  // 将要更新 ===> 调用beforeUpdate函数
            (重要)// 更新完毕 ===> 调用updated函数
                  // 将要销毁 ===> 调用beforeDestroy函数。 =====> 【重要的钩子】 总结：收尾的事情在这个钩子里面做
                  // 销毁完毕 ===> 调用destroyed函数


        



        // 练习(自动减少透明度，当透明度减少到0的时候重新变成1)
            // 模板代码
                // <div id="app">
                //     <div :style="{opacity}">{{message}}</div>
                //     <button @click="clear">clearTime</button>
                // </div>

            // 实例代码
                const vm = new Vue({
                    data() {
                        return {
                            message : 'Vue生命周期',
                            opacity : 1
                        }
                    },
                    mounted() {
                        this.clearTime = setInterval(() => {
                            this.opacity -= 0.01;
                            if(this.opacity <= 0) {
                                this.opacity = 1;
                            }
                            console.log('cleartime');
                        }, 16)
                    },
                    methods: {
                        clear() {
                            this.$destroy();
                        }
                    },
                    destroyed() {
                        clearInterval(this.clearTime);
                    },
                }).$mount('#app');



        // 常用的生命周期钩子：
            // 1.mounted：发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
            // 2.beforeDestroy：清除定时器、绑定自定义事件、取消订阅消息等【收尾工作】

        // 关于销毁Vue实例：
            // 1.销毁后借助Vue开发者工具看不到任何消息。
            // 2.销毁后自定义事件会失效，但原生DOM事件依然有效。
            // 3.一般不会在beforeDestroy操作数据，因为即便操作数据，也不会在触发更新流程了。











    // 25.Vue组件化编程
        // 组件的定义：实现应用中局部功能代码和资源的整合。

        // 1.模块
            // 1) 理解：向外提供特定功能的js程序，一般就是一个js文件
            // 2) 为什么：js文件很多很复杂
            // 3) 作用：复用js，简化js的编写，提高js运行效率


        // 2.组件
            // 1) 理解：用来实现局部(特定)功能效果的代码集合(html/css/js/images....)
            // 2) 为什么：一个界面的功能很复杂
            // 3) 作用：复用代码、简化项目编码、提高运行效率

    
        // 3.非单文件：一个文件中包含有n个组件。
        // 4.单文件组件：一个文件中只包含有1个组件。




        
        


        // 组件的使用(非单文件组件)
            // 模板代码
                // <div id="app">
                //     编写组件标签
                //     <student></student>
                //     <school></school>
                //     <school></school>
                // </div>

            // 实例代码
                // 创建student组件(局部组件，开发中局部组件用的多)
                let student = Vue.extend({
                    template : `
                        <div>
                            <p>{{name}}</p>    
                            <p>{{age}}</p>    
                        </div>
                    `,
                    data() {
                        return {
                            name : '张三',
                            age : 18
                        }
                    }
                })
            
                // 创建school组件
                let school = Vue.extend({
                    template : `
                        <div>
                            <p>{{name}}</p>    
                            <p>{{address}}</p>    
                        </div>
                    `,
                    data() {
                        return {
                            name : 'hylg',
                            address : 'china'
                        }
                    }
                })
            
                
            
                const vm = new Vue({
                    data : {
                        message : '组件的使用'
                    }, 
                    // 注册组件(局部注册)
                    components : {
                        school,
                        student
                    }
                }).$mount('#app');







            // 全局组件(可以在所有的实例上调用)
                // 模板代码
                    // <div id="app">
                    //     <student></student>
                    //     <school></school>
                    // </div>
                    // <br>
                    // <div id="root">
                    //     <student></student>
                    //     <school></school>
                    // </div>
                // 实例代码
                    Vue.component('school', {
                        template : `
                            <div>
                                <p>{{name}}</p> 
                                <p>{{address}}</p>
                            </div>
                        `,
                        data() {
                            return {
                                name : 'hylg',
                                address : 'china'
                            }
                        }
                    })



        
        // 非单文件组件总结：
            // Vue中使用组件的三大步骤：
                // 1.定义组件(创建组件)
                // 2.注册组件
                // 3.使用组件(写组件标签)

            // 1.如何定义一个组件？
                // 使用Vue.extend(options)创建，其中options和new Vue(options)时传入的那个options几乎一样，单页有区别：
                    // 区别如下：
                        // 1.el不要写，为什么？----最终所有的组件都要经过vm的管理，由vm中的el决定服务哪个容器。
                        // 2.data必须哪些函数，为什么？----避免组件被复用时，数据存在引用关系。
                        
                    // 备注：使用template可以配置组件结构。


            // 2.如何注册组件
                // 1) 局部注册：靠new Vue的时候传入components选项。
                // 2) 全局注册：靠Vue.component('组件名', 组件)

            // 3.编写组件标签
                // <school></school>



        // 几个注意点：
            // 1.关于组件的命名
                // 一个单词组成的：
                    // 第一种写法(首字母小写)：school
                    // 第二种写法(首字母大写)：School
                // 多个单词组成
                    // 第一种写法(kebab-case命名)：my-school
                    // 第二种写法(CamelClass命名)：Myschool(需要Vue脚手架支持,不然会报错)

                // 备注：
                    // (1) 组件名尽可能回避HTML中已有的元素名称，例如：h1,、H2都不行。
                    // (2) 可以使用name配置项指定组件在开发中工具呈现的名字。

            
            // 2.关于组件标签：
                // 第一种写法：<school></school>
                // 第二种写法：<school/>
                // 备注：不适用脚手架时  <school/>会导致后续组件不能渲染。


            // 3.一个简写方式
                // const school = Vue.extend(options) 可简写为: const school = options
                    




    



    // 26.组件的嵌套
        // 模板代码
            // <div id="app">
            //     <app></app>
            // </div>

        // 实例代码
            // 子组件student
            const student = Vue.extend({
                // name属性用来控制vue管理工具中组件的名称
                name : 'student',
                data() {
                    return {
                        name : 'zs',
                    }
                },
                template : `
                    <div>
                        <p>{{name}}</p>
                    </div>
                `
            })
            
            // 子组件hello
            const hello = Vue.extend({
                data() {
                    return {
                        hello : 'hello work'
                    }
                },
                template : '<p>{{hello}}</p>'
            })

            // 父组件school
            const school = Vue.extend({
                name : 'school',
                data() {
                    return {
                        name : '惠州城市学院',
                        address : '惠州市惠城区'
                    }
                },
                template : `
                    <div>
                        <p>{{name}}</p>    
                        <p>{{address}}</p>

                        <!-- 在这里编写组件标签 -->
                        <student></student>
                    </div>
                `,
                components : {
                    // 在这里注册组件
                    student,
                }
            })

            // 创建一个app组件(用于管理所有的组件)
            const app = Vue.extend({
                template : `
                    <div>
                        <school></school>
                        <hello></hello>    
                    </div>
                `,
                components : {
                    school,
                    hello
                }
            })

            // vm
            const vm = new Vue({
                components : {app}
            }).$mount('#app');




    


        // 关于VueComponent：
            // 1.school组件本质是一个名为VueComponent的构造函数，且不是程序员定义的，是Vue.extend生成的。
            // 2.我们只需要写<school></school>，Vue解析时会帮我们生成school组件的实例对象，
                // 即Vue帮我们执行了：new VueComponent(options)
            // 3.特别注意：每次调用Vue.extend，返回的都是一个全新的VueComponent！！！
            // 4.关于this指向：
                // (1) 组件配置中：
                    // data函数、methods中的函数、watch中的函数、computed中的函数 他们的this均是【VueComponent实例对象】。
                // (2) new Vue()配置中：
                    // data函数、methods中的函数、watch中的函数、computed中的函数，他们的this均是【Vue实例对象】。

            // 5.VueComponent的实例对象，以后简称vc(也称之为：组件实例对象)。
                // Vue实例对象，以后简称vm。


        // 一个重要的内置关系
            // 1.一个重要的内置关系：VueComponent.prototype.__proto__ === Vue.prototype
            // 2.为什么要有这个关系:让组件实例对象(vc) 可以访问到Vue原型上的属性、方法。

    






    // 27.单文件组件
        // 安装Vetur插件
        // 快速生成模板：先输入 <vue> 按回车



    // 28.ref(给dom元素打标识)
        // 自己写的总结：
            // 1.如果在html标签里面添加ref属性的，获取到的就是dom元素
            // 2.如果在组件标签里面添加ref标识，获取到的就是组件实例
            // 怎么获取ref标记
                // 可以通过this.$refs.标记名称来来获取

            // 例如(1.我要获取元素，2.我要获取组件实例)
                // <template>
                //     <div>
                //         <Student ref='student'/>
                //         <School/>
                //         <h1 ref='tag' v-text='message'></h1>
                //         <button ref='that' @click='output'>点我输出上方的标签</button>
                //     </div>
                // </template>
                
                /*
                <script>
                    import Student from './components/Student.vue'
                    import School from './components/School.vue'
                    export default {
                        name : 'App',
                        data() {
                            return {
                                message : 'Vue'
                            }
                        },
                        components : {
                            Student,
                            School
                        },
                        methods : {
                            output() {
                                console.log(this.$refs.student); //获取组件实例
                                console.log(this.$refs.tag)  //获取h1标签
                            }
                        }
                    }
                </script>
                */

        // 视频总结：
            // 1.被用来给元素或子组件注册引用信息(id代替者)
            // 2.应用在html标签上获取的是真实dom，应用在组件标签上获取的是组件实例对象(VueComponents)
            // 3.使用方法：
                // 打标识：<h1 ref='xxx'>....</h1> 或 <School ref='xxx'/>
                // 获取：this.$refs.xxx
            
        
        






    // 29.props配置项
        // props总结：
            // 功能：让组件接收外部穿过来的数据。
                // (1).传递数据
                    // <Demo name="xxx"/>
                // (2).接收数据
                    // 第一种方法：
                        // props: ['name']
                    // 第二种方法(限制类型)
                        /*
                        props: {
                            name:String
                        }
                        */
                    // 第三种方法(限制类型、限制必要性、指定默认值)
                        /*
                            props: {
                                name: {
                                    type:String,   //限制类型
                                    required: true,  //必要性(必须要传不传就报错)
                                    default:'老王'   //默认值
                                }
                            }
                        */
                // 备注：props是只读，Vue底层会检测你对props的修改，如果进行修改，就会发出警告，
                        // 若业务需求确实需要修改，那么请负值props的内容到data中一份，然后去修改
                        // data中的数据。



        // 例如：我要创建一个学生组件，组件里面有学校，姓名、年龄、性别
            // 学校名称是固定的
            // 姓名、年龄、性别是根据情况而定的
            // 这个时候我们就需要用到props配置项了



            // 组件代码
                /* 
                <template>
                    <div class="Student">
                        <h1>School：{{school}}</h1>
                        <h3>name：{{name}}</h3>
                        <h3>age：{{age+1}}</h3>
                        <h3>sex：{{sex}}</h3>
                    </div>
                </template>

                <script>
                    export default {    
                        name : 'Student',
                        data() {
                            return {
                                school : 'HYLG',
                            }
                        },
                        //简单接收(开发当中使用这个多一点)
                        // props : ['name', 'sex', 'age'] 


                        // 接收的同时对类型进行限制
                        // props : {
                        //     name:String,
                        //     age:Number,
                        //     sex:String
                        // }




                        // 接收的同时：进行类型限制+默认值的指定+必要性的限制(在这个案例里面我们使用的是这种写法)
                        props : {
                            name: {
                                type:String,
                                required:true //名字是必要的,如果必要的你不传的话就会报错
                            },
                            age: {
                                type:Number,
                                default:99  //默认值，你传了就是用你传的，不传就是用默认值
                            },
                            sex: {
                                type:String,
                                required:true  //一般情况下require和default两个不会同时出现
                            }
                            
                        }
                    }
                </script>
                */
            // App.vue代码
                /*
                    <template>
                        <div>
                            <Student name='张三' :age='20' sex='famle'/>
                        </div>
                    </template>
                    <script>
                        import Student from './components/Student.vue'
                        export default {
                            name : 'App',
                            data() {
                                return {
                                    message : 'Vue'
                                }
                            },
                            components : {
                                Student,
                            }
                        }
                    </script>
                */




    // 30.mixin(混入)
        // 总结：
            // 功能：可以吧多个组件共用的配置提取成一个混入对象
            // 使用方法：
                // 第一步定义混合：例如：
                    /*
                    export const hunhe{
                        data() {
                            return {

                            }
                        },
                        methods: {
                            ....
                        }
                    }
                    */
                // 第二步使用混入，例如：
                    // (1)全局引入：Vue.mixin(xxx)
                    // (2)局部混入：mixings:['xxx']


        // 多个组件共享一个配置，前提是这个配置得是相同的(复用配置)

        /*
        例如(局部混合)：我有两个组件，一个组件叫School另外一个组件叫Student，
            他们两个有一个相同的方法，现在我需要把他抽离出来，放在一个
            新的文件里面(这个新的文件叫mixin.js文件名称可以自定义)并且
            把他export出去，然后我在School和Student需要用的时候，
            需要先import导入，然后在mixins配置项里面的数组中写你import
            进来的名称，然后就可以在组件中使用了。
        */

            // Student组件代码
            /*
                <template>
                    <div>
                        <h3 @click='showName'>{{name}}</h3>
                        <h3>{{age}}</h3>
                        <h3>{{sex}}</h3>
                    </div>
                </template>

                <script>
                    import {mixin} from '../mixin'
                    export default {
                        data() {
                            return {
                                age : 18,
                                sex : 'male',
                                name : 'ZS'
                            }
                        },
                        mixins: [mixin]
                    }
                </script>
            */



        

            // School组件代码
            /*
                <template>
                    <div>
                        <h1 @click="showName">{{name}}</h1>
                        <h3>{{address}}</h3>
                    </div>
                </template>

                <script>
                    // 引入一个混合
                    import {mixin} from '../mixin'
                    export default {
                        data() {
                            return {
                                name : 'HYLG',
                                address : '广东省河源市源城区'
                            }
                        },
                        mixins: [mixin]
                    }
                </script>
            */



            // mixin.js代码
            /*
                export const mixin = {
                    methods: {
                        showName() {
                            alert(this.name);
                        }
                    },
                }
            */
        
        
        // 



    // 31.插件(没练习68集)
        // 功能：用于增强Vue
            // 本质：包含install方法的一个对象，install第一个参数是Vue，第二个以后的参数是插件使用者传递的数据。
            
            // 定义插件：
                /*
                对象.install = function(Vue, options) {
                    1.添加全局过滤器
                    Vue.filter(......)

                    2.添加全局指令
                    Vue.directive(......)

                    3.配置全局混入
                    Vue.mixin(......)

                    4.添加实例方法
                    Vue.prototype.$myMethod = function() {....}
                    Vue.prototype.$myProperty = xxx
                }
                */


        // 例如：
            // 1.定义插件(在src目录下新建一个plugins.js文件)
                export default {
                    install(Vue) {
                        console.log('@@@@install', Vue);
                        // 这里面就可以写过滤器、全局指令、全局混入等等......
                    }
                }
            // 2.在main.js里面导入插件
                import plugin from './plugins'
            // 3.在main.js里面使用插件
                Vue.use(plugin)
    


    // 32.scoped样式
        // 总结：
            // scoped的作用：让样式在局部生效，防止类名冲突
                // 写法：<style scoped></style>


        // App文件里面的style不适合使用scoped

        /* 组件中的lang="这里可以写css或者是less" scoped是作用域的意思
        <style scoped lang='less'>
            .demo{
                background-color: orange;
                h1{
                    font-size: 15px;
                    font-family: 楷体;
                }
                h3{
                    font-size: 10px;
                    color: red;
                    font-family: 华文彩云;
                }
            }
        </style>
        */





    // ToDo-list案例：
        // 组件化编码流程(通用)
            // 1.实现静态组件：抽取组件，使用组件实现静态效果
            // 2.展示动态数据：
                // 1) 数据的类型、名称是什么？
                // 2) 数据保存在哪个组件？
            // 3.交互————从绑定事件监听开始
            


    // ToDo-list案例总结：
        // 1.组件化编码流程
            // (1). 拆分静态组件：组件按照功能点拆分，命名不要与html元素冲突。
            // (2). 实现动态组件：考虑好数据的存放位置，数据是一个组件在用，还是一些组件再用：
                // 1). 一个组件再用：放在组件自身即可。
                // 2). 一些组件再用：放在他们共同的父组件身上(状态提升)。
            // (3). 实现交互：从绑定事件开始

        // 2.props适用于：
            // (1). 父组件===>子组件 通信
            // (2). 子组件===>父组件 通信 (要求父先给子一个函数用参数的形式传递数据)
        // 3.使用v-model时要切记：v-model绑定的值不能是props传过来的值，因为props是不可以修改的！
        // 4.props传过来的若是对象类型的值，修改对象中的属性时Vue不会报错，但不推荐这样做。








    // 33.组件自定义事件
        // 组件自定义事件总结：
            // 1.一种组件间通信的方式，适用于子组件给父组件传输数据 ：  子组件=======》父组件
            // 2.使用场景：A是父组件，B是子组件，B想给A传数据，那么就要在A中给B绑定自定义事件(事件回调在A中)。
            // 3.绑定自定义事件：
                // 1).第一种方式：在父组件中: <Demo @getData='demo' /> 或者 <Demo v-on:getData='demo'/>
                // 2).第二种方式：在父组件中
                    /*
                        <Demo ref='demo'/>
                        .......
                        mounted() {
                            this.$refs.demo.$on('getData', this.test)
                        }
                    */
                // 3).若想让自定义事件只能触发一次，可以使用once修饰符，或$once方法。
            
            // 4.触发自定义事件：this.$emit('getData', 数据)
            // 5.解绑自定义事件：this.$off('getData')
            // 6.组件上也可以绑定原生DOM事件，需要使用native修饰符。
            // 7.注意：通过this.$refs.demo.$on('getData', 回调) 绑定自定义事件，回调要么配置在methods中，要么用箭头函数，否则this指向会出问题

        // 1.绑定自定义事件
            // 例如：通过父组件给子组件绑定一个自定义事件：子组件给父组件传递数据(第一种写法@或v-on)
                // 第一步：在组件标签上写 <Student @事件名='事件回调'/>
                    // <Student @getName='demo'/>

                // 第二步：在methods里面写自定义事件的事件回调。
                    /*
                    demo(name) {
                        console.log(name);
                    }
                    */

                // 第三步：打开Student组件,当点击button按钮的时候触发getName事件
                    /*
                    <template>
                        <div class="demo">
                            <h3>Student</h3>
                            // 1) 点击button调用emit方法
                            <button @click='emit'>getName</button>
                        </div>
                    </template>
                    
                    <script>
                        export default {
                            data() {
                                return {
                                    name : 'ZS',
                                }
                            },
                            methods: {
                                emit() {
                                    // 可以使用$emit('自定义事件名', 后面的是参数) 这个方法调用自定义事件
                                    this.$emit('getName', this.name);
                                }
                            }
                        }
                    </script>
                    */


            // 例如：通过父组件给自子组件绑定一个自定义事件实现：子给父传递数据(第二种写法，使用ref获取组件实例进行事件绑定)
                // 第一步：使用ref获取组件实例
                    // <Student ref='student'/>

                // 第二步：在mounted这个钩子里面绑定事件  this.$refs.student.$on('自定义事件名称', 事件回调)
                    // 回调的第一种写法：写成普通函数this指向调用者，写成箭头函数this指向所在的组件实例
                    this.$refs.student.$on('getName', () =>{
                        console.log(this);         
                    })
                    // 回调的第二种写法(使用methods里面的方法)
                    this.$refs.student.$on('getName', this.demo)  //这里的this指向父组件实例

                    // 绑定一次性自定义事件可以使用$once这个api
                    this.$refs.student.$once('getName', this.demo)

                // 第三步和上面的写法相同






        // 解绑自定义事件
            // 例如：我需要点击button解绑一个自定义事件
                // 模板代码
                    // <button @click='unbind'>解绑自定义事件getSchoolData</button>
            
                // 实例代码(解绑单个事件)：
                    /*
                    methods: {
                        unbind() {
                            this.$off('getSchoolData') //解绑单个事件
                            this.$off(['getSchoolData', 'getSchool']) /解绑多个事件
                            this.$off()  //解绑所有事件
                        }
                    }
                    */

                // 注意：
                    // 如果销毁了组件实例，销毁后该组建中所有的自定义事件全部都不奏效了。





        // 给组件绑定原生事件
            // 需要加上native修饰符，不然组件标签会把该事件当成自定义事件
            // <School @click.native='show'/>

                




        





    // 34.全局事件总线
        // 总结：
            // 1.一种组件间通信的方式，适用于任意组件间通信。
            // 2.安装全局事件总线：
                new Vue({
                    beforeCreate() {
                        Vue.prototype.$bus = this;   //安装全局事件总线.$bus就是当前应用的vm
                    }
                })

            // 3.使用事件总线:
                // 1) 接收数据，A组件想接收数据，则在A组件中给$bus绑定自定义事件，事件的回调留在A组件自身
                    /*
                    methods: {
                        demo(data) {console.log(a)}
                    }
                    */
                // 2) 提供数据: this.$bus.$emit('xxxx', 数据)
                    

            // 4.最好在beforeDestroy钩子中，用$off去解绑当前所用到的事件。
            
        


        // 例如：两个兄弟组件要相互通信
            // 1.先在main.js中的vm实例中的beforeDestroy这个钩子里面安装全局事件总线
                /*
                beforeCreate() {
                    Vue.prototype.$bus = this;   //安装全局事件总线
                }
                */
            // 2.在school组件中创建自定义事件(数据接收方)
                /*
                mounted() {
                    this.$bus.$on('getData', (data) => {
                        console.log(data);
                    })
                }
                */
            // 3.在student组件中创建一个方法，当点击某个按钮的时候触发该方法
                /*
                methods: {
                    sendData() {
                        this.$bus.$emit('getData', this.name)
                    }
                }
                */
            



                    



    // 35.消息订阅与发布pubsub (订阅：需要数据的一方     发布：发送数据的一方)
        // 总结：
            // 1.一种组件间通信的方式，用于任意组件间通信。(建议使用全局事件总线)
            // 2.使用步骤
                // 1.安装pubsub: npm i pubsub-js
                // 2.引入：import pubsub from 'pubsub-js'
                // 3.接收数据：A组件想要接收数据，则在A组件中订阅消息，订阅的回校留在A组件自身。
                    /*
                    methods() {
                        demo(messageName, data) {
                            console.log(`订阅消息名：${messageName}   数据：${data}`)
                        }
                        mounted() {
                            this.pid = pubsub.subscribe('xxxx', this.demo);  //订阅消息
                        }
                    }
                    */
                // 4.提供数据：pubsub.publish('xxxx', 数据)
                // 5.最高在beforeDestroy(即将被销毁的钩子中)钩子中，用pubsub.unsubscribe(pid) //取消订阅



        // 1.需要下载第三方的库实现消息的订阅与发布(pubsubj-js)
            // 库名：pubsub-js
            // npm i pubsub-js

        // 2.引入pubsub-js库
            import pubsub from 'pubsub-js'
import { ModuleFilenameHelpers } from 'webpack';
            // 引入的pubsub是一个对象

        // 3.订阅消息(在数据的接收方配置) 
            /*
            mounted() {
                                           //这里可以收到两个参数 1.消息名  2.数据
                pubsub.subscribe('getData', (messageName, data) => {
                    console.log(this);
                })
            }
            */
        // 4.消息的发布(在发送数据的一方配置)
            this.pubid = pubsub.publish('getData', this.name)
        // 5.取消订阅(可选)
            // 订阅消息类似于定时器，它会返回一个id，如果要取消的话需要用到这个id
            pubsub.unsubscribe(this.pubid)







    // 36.nextTick
        // 1.语法：this.$nextTick(回调函数)
        // 2.作用：在下一次DOM更新结束后执行其指定的回调。
        // 3.什么时候调用：当改变数据后，要基于更新后的DOM进行某项操作时，要在nextTick所指定的回调中去执行。
            // 例如：
            this.$nextTick(function() {
                this.$refs.input[0].focus();
            })










    // 37.vue动画
        // 例如(实现方法1)：实现一个元素点击向左向右切换的效果
            // <template>
            //     <div>
            //         <button @click="demo">显示/隐藏</button>
                      //appear：设置节点在渲染的时候过渡
            //         <transition name="hello" appear>
            //             <h1 v-show="isShow">JavaScript</h1>
            //         </transition>
            //     </div>
            // </template>
            
            // <script>
            //     export default {
            //         name: 'School',
            //         data() {
            //             return {
            //                 isShow: true,
            //                 key: true
            //             }
            //         },
            //         methods: {
            //             demo() {
            //                 this.isShow = !this.isShow;
            //             }
            //         }
            //     }
            // </script>
            
            // <style lang='less' scoped>
            //     h1{
            //         background-color: orange;
            //     }
                    // 进来的动画 名字-leave-active
            //     .hello-enter-active{
            //         animation: ease toggle 1s;
            //     }
                    // 离开的动画 名字-leave-active
            //     .hello-leave-active{
            //         animation: ease toggle 1s reverse;
            //     }
            //     @keyframes toggle{
            //         from{
            //             transform: translateX(-100%);
            //         }to{
            //             transform: translateX(0px);
            //         }
            //     }
            // </style>
            

        // 例如(实现方法2)：实现一个元素点击向左向右切换的效果
            // <template>
            //     <div class="wrapper">
            //         <button @click="toggle">toogle</button>
                    //  transition标签里面只能只能有一个标签，如果要在里面放多个标签请使用transition-group并且把里面的标签都加上一个key值(key的值是唯一的)
            //         <transition name="hello" appear>
            //             <div v-show='switchSchool' class="content">webPack</div>
            //         </transition>
            //     </div>
            // </template>
            // <script>
            //     export default {
            //         data() {
            //             return{
            //                 switchSchool: true,
            //             }
            //         },
            //         methods: {
            //             toggle() {
            //                 this.switchSchool = !this.switchSchool;
            //             }
            //         }
            //     }
            // </script>
            // <style lang="less" scoped>
            //     .wrapper{
            //         margin-top: 50px;
            //         display: flex;
            //         flex-direction: column;
            //         button{
            //             width: 100px;
            //             height: 50px;
            //         }
            //         .content{
            //             width: 100px;
            //             height: 100px;
            //             border: 1px solid black;
            //             border-radius: 50%;
            //             display: flex;
            //             justify-content: center;
            //             align-items: center;
            //             background-color: #ccc;
            //             transition: ease-in-out 1s;
            //         }
            //     }
            


            //     没优化的时候
            //     // 进入的起点
            //     .hello-enter{
            //         transform: translateX(-100%);
            //     }
            //     // 进入的终点
            //     .hello-enter-to{
            //         transform: translateX(0px);
            //     }
            //     // 离开的起点
            //     .hello-leave{
            //         transform: translateX(0px);
            //     }
            //     // 离开的终点
            //     .hello-leave-to{
            //         transform: translateX(-100%);
            //     }


                // 优化后
                // 当hello-enter-active 和 hello-leave-active 激活的时候给它们加上过渡
                // .hello-enter-active,.hello-leave-active{
                //     transition: ease-in-out 1s;
                // }
                // // 进入的起点和离开的终点
                // .hello-enter,
                // .hello-leave-to{
                //     transform: translateX(-100%);
                // }
                // // 进入的终点和离开的起点
                // .hello-enter-to,
                // .hello-leave{
                //     transform: translateX(0px);
                // }
            // </style>
            
        


        // 集成第三方动画库(animate.css推荐)
            // 网站地址 https://animate.style/

            // 1.安装：npm install animate.css --save
            // 2.引入该库(引入css的方法) ：import 'animate.css'
            // 3.给transition的name属性写上animate__animated animate__bounce
                // <transition
                //     name="animate__animated animate__bounce"
                //     appear
                // 4. 进入的动画
                //     enter-active-class="animate__rubberBand"
                // 5.离开的动画
                //     leave-active-class="animate__heartBeat"
                // >
                //     <div v-show='switchSchool'>webPack</div>
                // </transition>






    // 38.脚手架代理配置
        // vue脚手架配置代理总结：
            // 方法一：
                // 在vue.config.js中添加如下配置：
                    devServer: {
                        proxy: 'http://localhost:5000'
                    }
                // 说明：
                    // 1.优点：配置简单，请求资源直接发送给前端8080即可。
                    // 2.缺点：不能配置多个代理，不能灵活的控制请求是否走代理。
                    // 3.工作方式：若按照上述配置代理，当请求了前端不存在的资源时，那么该请求会转发给服务器(优先匹配前端资源)


            // 方法二：
                // 编写vue.config.js规则如下
                modele.exports = {
                    devServer: {
                        proxy: {
                            '/users': { //匹配所有以user开头的请求路径
                                target: 'http://localhost:5000', //代理目标的基础路径
                                pathRewrite: {'^/users': ''},
                                ws: true,
                                changeOrigin: true
                            },
                            '/cars': { //匹配所有以user开头的请求路径
                                target: 'http://localhost:5001', //代理目标的基础路径
                                pathRewrite: {'^/cars': ''},
                                ws: true,
                                changeOrigin: true
                            }
                        }
                    }
                }
                // changeOrigin设置为true时，服务器接收到的请求头中的host为：localhost:5000
                // changeOrigin设置为false时，服务器收到的请求头中的host为，localhost:8080
                // changeOrigin默认为true


                // 说明：
                    // 1.优点：可以配置多个代理，且可以灵活的控制请求是否走代理。
                    // 2.缺点：配置略微繁琐，请求资源时必须加上前缀。




            




        // 方式一(在vue.config.js配置)
            // 例如：我需要获取数据，数据在http://localhost:5000，浏览器存在跨域问题，
                // 需要配置代理
                    devServer: {
                        proxy: 'http://localhost:5000'
                    }

                // 获取数据
                    axios.get('http://localhost:8080/students').then(response => {
                        console.log(response.data);
                    }).catch(error => {
                        console.log(error);
                    })
    


        // 方式二：
            // 开启代理(方式二)
                // 配置代理
                    /*
                    devServer: {
                        proxy: {
                        '/user': {
                            target: 'http://localhost:5000',
                            pathRewrite: {'^/user' :  ''},   //重写路径，当请求的时候携带/user路径的时候，把这个路径变成空，向目标服务器请求时不要携带 /user
                            ws: true,   //ws指的是:websocket，ws这个功能用于支持websocket
                            changeOrigin: true   //是否告诉请求服务自己来自于哪里(true：说谎(说他也是来自：http://localhost:5000))(false：告诉请求服务器自己真实的地址)，默认就是true
                        },
                        '/cars': {
                            target: 'http://localhost:5001',
                            pathRewrite: {'^/cars': ''}
                        }
                        }
                    }
                    */


                // 获取数据
                    axios.get('http://localhost:8080/user/students').then(response => {
                        console.log(response.data);
                    }).catch(error => {
                        console.log(error.message);
                    })
                    axios.get('http://localhost:8080/cars/cars').then(response => {
                        console.log(response.data);
                    }).catch(error => {
                        console.log(error);
                    })



        // 完成github用户搜索案例


        // 发送网络请求的方法
            // 1.xhr
            // 2.jQuery 
            // 3.axios
            // 4.fetch
            // 5.vue-resource

    // 39.vue-resource(vue中的插件库 官方已经不维护了)(了解即可vue推荐使用axios))
        // 下载插件 
            // npm i vue-resource
        
        // 引入插件
            import vueResource from 'vue-resource'
        
        // 使用插件
            Vue.use(vueResource)

        // 获取数据
            this.$http.get('https://api.github.com/search/users?q=user').then(response => {
                console.log(response.data);
            }).catch(error => {
                console.log(error);
            })






    // 40.solt插槽
        // 插槽的总结：
            // 1.作用：让父组件可以向子组件指定位置插入html结构，也是一种组件间通信的方式。适用于 父组件====>子组件
            // 2.分类：默认插槽、具名插槽、作用域插槽
            // 3.使用方式：
                // 1) 默认插槽
                    // 父组件中：
                        // <Category>
                        //     <div>html结构1</div>
                        // </Category>

                    // 子组件中
                        // <template>
                        //     <div>
                        //         定义插槽
                        //         <slot>插槽的默认内容</slot>
                        //     </div>
                        // </template>



                // 2) 具名名插槽
                    // 父组件：
                        // <Category>
                        //     <template slot='center'>
                        //         <div>html结构1</div>
                        //     </template>
                        //     <template vster>
                        //         <div>html结构2</div>
                        //     </template>
                        // </Category>
                    // 子组件
                        // <template>
                        //     <slot name='center'>插槽的默认内容</slot>
                        //     <slot name='footer'>插槽的默认内容</slot>
                        // </template>



                // 3) 作用域插槽
                    // 1.理解：数据在组件的自身，但根据数据生成不同的结构需要组件的使用者来决定。(games数据在Category组件中，但是用数据所遍历出来的结构由App决定)
                    // 2.具体编码：
                        // 父组件:
                            // <Category>
                            //     <template scope='data'>
                            //         生成的是ul列表
                            //         <ul>
                            //             <li v-for='g in data.games'>{{g}}</li>
                            //         </ul>
                            //     </template>
                            // </Category>

                            // <Category>
                            //     <template scope='data'>
                            //         生成的是h4标签
                            //         <ul>
                            //             <h4 v-for='g in data.games'>{{g}}</h4>
                            //         </ul>
                            //     </template>
                            // </Category>


                        // 子组件：
                            // <template>
                            //     <div>
                            //         <slot :games='game'></slot>
                            //     </div>
                            // </template>


                            // <script>
                            //     export default {
                            //         name: 'Category',
                            //         data() {
                            //             return {
                            //                 games: ['王者荣耀', '穿越火线', '超级玛丽', '天天酷跑', '吃鸡'],
                            //             }
                            //         }
                            //     }
                            // </script>





        // 方式一、默认插槽案例：
            // 1.在子组件中写
                // <ul>
                //     <!-- 定义一个插槽 -->
                //     <slot>我是一些默认值，当使用者没有传具体结构的时候我会出现</slot>
                // </ul>

            // 2.父组件写法
                // <Demo category='食物' :listData='foods'>
                    // 子组件slot插槽就会变成组件标签里面的内容
                //     <img src="https://img1.baidu.com/it/u=925657677,3910954268&fm=26&fmt=auto" alt="">
                // </Demo>
                


        // 方式二、具名插槽(意思：具有名字的插槽)
            // 1.给子组件的插槽定义名字
                // <ul>
                //     <slot name="center">center</slot>
                //     <slot name="footer">footer</slot>
                // </ul>
            

            // 2.在父组件使用子组件的标签里面写上结构并且写上哪个结构对应哪个插槽
                // <Demo>
                //     <img  slot='center' src="https://img1.baidu.com/it/u=925657677,3910954268&fm=26&fmt=auto" alt="">
                //     <span slot="footer">食物图片</span>
                // </Demo>




        // 方式三、作用域插槽(就是把子组件中的数据传给插槽的使用者)
            // 子组件代码
                // <template>
                //     <div>
                //         <h3>{{category}}-类</h3>
                //         <!-- 作用域插槽：  :data='foods'的作用就是把foods这个数据发送给插槽的使用者 -->

                        // 我需要把data中的foods这个数组传输给插槽的使用者
                //         <slot :data='foods'>默认的内容</slot>
                //     </div>
                // </template>
                
                // <script>
                //     export default {
                //         name: 'Demo',
                //         props: ['category'],
                //         data() {
                //             return {
                //                 foods: ['火锅', '烧烤', '小龙虾', '牛排', '生蚝']
                //             }
                //         }
                //     }
                // </script>



            // 父组件代码
                // <Demo category='食物'>
                    // 接收子组件传输过来的数据必须写在template里面    旧的api：scope   新的api：slot-scope
                //     <template scope='allData'>
                //         <ul>
                                    // 遍历子组件传输过来的数据
                //             <li v-for='value in allData.data' :key='value'>{{value}}</li>
                //         </ul>
                //     </template>
                // </Demo>

        







    // 41.vuex
        // Vuex总结：
            // 1.概念：
                // 专门在Vue中实现集中式状态(数据)管理的一个Vue插件，
                //  对vue应用中多个组件的共享组件状态几种式的管理(读写)，
                //  也是一种组件间通信的方式，且用于任意组件间通信。
            // 2.何时使用：
                // 多个组件需要共享数据时
            // 3.搭建vuex环境
                // 1.下载vuex： npm install vuex

                // 2.创建文件: src/store/index.js
                    // 引入文件
                        import Vue from 'vue'
                    // 引入Vuex
                        import Vuex from 'Vuex'
                    // 应用Vuex插件
                        Vue.use(Vuex)
                    // 准备actions对象————响应组件用户的动作
                        const actions = {}
                    // 准备mutations对象————修改state中的数据
                        const mutations = {}
                    // 准备state对象————保存具体的数据
                        const state = {}
                    // 创建并暴露store
                        export default new Vue({
                            actions,
                            mutations,
                            getters,
                            state
                        })
                
                // 3.在main.js中创建vm时传入store配置项
                    // 引入store
                        import store from './store/index.js'
                    // 创建vm
                        new Vue({
                            render: h = h(App),
                            store
                        }).$mount('#app')

            

            // 4.基本使用
                // 1.初始化数据、配置actioins、配置mutations、操作文件store.js
                    // 引入Vue核心库
                    import Vue from 'vue'
                    // 引入vuex
                    import vuex from 'vuex'
                    Vue.use(vuex);
                    const actions = {
                        // 响应组件动作
                        increase(minStore, value) {
                            minStore.commit('INCREASE', value);
                        }
                    }
                    const mutations = {
                        INCREASE(state, value) {
                            state.sum += value;
                        }
                    }
                    const state = {
                        sum: 0
                    }
                    export default new Vuex.Store({
                        actions,
                        mutations,
                        state
                    })
                        
                // 2.组件中读取Vuex的数据： $store.state.sum
                // 3.组件中修改Vuex的数据： $store.dispatch('actions中的方法名', 数据) 或者 $store.commit('mutations中的方法名', 数据)
                // 备注：
                    // 若没有网络请求或其他业务逻辑，组件中可以越过actions，即不写dispatch，直接编写commit

            


            // 5.getter的使用
                // 1.概念：当state中的数据需要经过加工在使用时，可以使用getter加工。
                // 2.在store.js中追加getters配置
                    const getters = {
                        bigSum(state) {
                            return state.sum * 10;
                        }
                    }
                    export default new Vuex.Store({
                        // .....
                        getters
                    })
                // 3.组件中读取数据：$store.getters.bigSum




            // 6.四个map方法的使用
                import {mapState, mapGetters, mapActions, mapMutations} from 'vuex'
                // 1.mapState方法：用于帮助我们映射state中的数据为计算属性
                    /*
                    compted: {
                        // 借助mapState生成计算属性，sum、school、subject  (对象写法)
                        ...mapState({
                            sum: 'sum',
                            school: 'school',
                            subject: 'subject'
                        })
                        // 借助mapState生成计算属性，sum、school、subject (数组写法)
                        ...mapState(['sum', 'school', 'subject'])
                    }
                    */
                   
                // 2.mapGetters方法：用于帮助我们映射getters中的数据为计算属性
                    /*
                    compted: {
                        // 借助mapGetters生成计算属性，bigSum (对象写法)
                        ...mapGetters({
                            bigSum: 'bigSum'
                        })
                        // 借助mapGetters生成计算属性，bigSum (数组写法)
                        ...mapGetters(['bigSum'])
                    }
                    */

                // 3.mapActions方法：用于帮助我们生成actioins对话的方法，即：包含$store.dispatch(xxx)函数
                    /*
                    methods: {
                        // 靠mapActions生成，incrementOdd、increaseWait (对象形式)
                        ...mapActions({
                            increaseOdd: 'increaseOdd',
                            increaseWait: 'increaseWait'
                        })

                        // 靠mapActions生成，incrementOdd、increaseWait (数组形式)
                        ...mapActions(['increaseOdd', 'increaseWait'])
                    }
                    */

                // 4.mapMutations方法：用于帮助我们生成mutations对话的方法，即：包含$store.commit(xxx)函数
                    /*
                    methods: {
                        // 靠mapMutationss生成，increment、reduce (对象形式)
                        ...mapMutations({
                            increase: 'increase',
                            reduce: 'reduce'
                        })
                        // 靠mapMutationss生成，increment、reduce (数组形式)
                        ...mapMutations(['increase', 'reduce'])
                    }
                    */

                // 备注：mapActions和mapMutations使用时，若需要传递参数需要：在模板中绑定事件时传递好参数，否则参数就是事件对下你给。



            // 7.模块化+命名空间
                /*
                // 1) 目的：让代码更好维护，让多种数据分类明确
                // 2) 修改store.js
                    const countAbout = {
                        namespaced: true, //开启命名空间
                        state: {x: 1},
                        actions: {...},
                        mutations: {...},
                        getters: {
                            bigSum(state) {
                                return state.sum * 10;
                            }
                        }
                    }


                    const personAbout = {
                        namespaced: true, //开启命名空间
                        state: {...},
                        actions: {...},
                        mutations: {...},
                        getters: {}
                    }
                
        

                    export default new Vuex.Store({
                        personAbout,
                        countAbout
                    })

                // 3.开启命名空间后，组件中读取state数据：
                    // 方式一：自己直接读取
                        this.$store.state.personAbout.list
                    // 方式二：借助mapState读取
                        ...mapState('countAbout', ['sum', 'school', 'subject'])

                // 4.开启命名空间之后，组件读取getter数据：
                    // 方式一：自己读取
                        this.$store.getters['personAbout/firstName']
                    // 方式二：借助mapGetters读取
                        ...mapGetters('countAbout', ['bigSum'])
        
                // 5.开启命名空间后，组件调用dispatch
                    // 方式一：自己调用
                        this.$store.dispatch('personAbout/addPersonWang', person)
                    // 方式二：借助mapActions
                        ...mapActions('personAbout', ['increaseOdd', 'increaseWait'])

                // 6.开启命名空间之后，组件调用commit
                    // 方式一：自己调用
                        this.$store.commit('personAbout/ADD_PERSON', person)
                    // 方式二：借助mapMutations
                        ...mapMutations('personAbout', ['ADD_PERSON'])

                */

    // 42.路由(router)
        // 1.vue-router理解
            // vue的一个插件库，专门用来实现spa应用

        // 2.对spa应用的理解
            // 1) 单页面Web应用(single page web application (spa))
            // 2) 整个应用只有一个完整的页面。 index.html
            // 3) 点击页面中的导航连接不会刷新页面，只会做页面局部的刷新
            // 4) 数据需要通过ajax请求获取
            
        // 3.路由的理解
            // 1) 什么是路由?
                // 路由就是一组映射关系(key - value)
                // key为路径，value可能是 function 或 components
                // 
            // 2) 路由分类
                // 后端路由：
                    // 理解：value是function，用于处理客户提交的请求。
                    // 工作过程：服务器接收到一个请求时，根据请求路径找到匹配的函数来处理请求，返回响应数据。
                    
                // 前端路由
                    // 理解：value是component，用于展示页面内容。
                    // 工作过程：当前浏览器的路径改变时，对应组件就会显示。
                    



        // 4.Vue路由的配置方法
            // 1) npm install vue-router  (安装vue-router)
            // 2) import VueRouter from 'vue-router'  (在main.js中导入)
            // 3) Vue.use(VueRouter);     (使用该插件)
            // 4) src/router/index.js     (创建)
            // 5) 在router/index.js下编写配置项：
                // 该文件用于创建整个应用的路由器
                import VueRouter from 'vue-router'
                // 引入About和Home
                import About from '../components/About.vue'
                import Home from '../components/Home.vue'
                // 创建路由器并暴露
                export default new VueRouter({
                    routes: [
                        {
                            path: '/About',
                            component: About
                        },
                        {
                            path: '/Home',
                            component: Home
                        }
                    ]
                })
            // 6) import router from './router/index.js'  (在main.js引入路由器)
            // 7) new Vue({router})        (在new Vue里面传入router配置)
            // 8) <router-link active-class="active" to='/About'>About</router-link> (实现切换 (active-class可配置高亮样式))(router-link标签最终会转换成a标签)
            // 9) <router-view></router-view> (指定组件的呈现位置)
            // 路由的注意点：
                // 1.路由组件通常存放在page文件夹，一般组件通常存放在components文件夹。
                // 2.通过切换，‘隐藏’了的路由组件，默认是被销毁的。需要的时候再去挂载。
                // 3.每个组件都有自己的$route属性，里面存储这自己的路由信息。
                // 4.整个应用只有一个router，可以通过组件的$router属性获取到。

        


        // 5.多级路由
            // 1.配置路由规则，使用children配置项：
                /*
                routers: [
                    {
                        path: '/about',
                        component: About
                    },
                    {
                        path: '/home',
                        component: Home,
                        children: [  //通过children配置子级路由
                            {
                                path: 'news' //此处一定不要写 /news
                                component: News,
                            },
                            {
                                path: 'message', //此处一定不要写 /message
                                component: Message
                            }
                        ]
                    }
                ]
                */

            // 2.跳转(要写完整路径)
                // <router-link to='/home/news'>News</router-link>



        // 6.路由的query参数
            // 1.传递参数
                // 跳转并携带query参数，to字符串写法
                    // <router-link :to="`/About/About_1/detail?id=${value.id}&title=${value.title}`">{{value.title}}</router-link>
                
                // 跳转并携带query参数，to对象写法
                    /*
                    <router-link :to="{
                        path: '/About/About_1/detail',
                        query: {
                            id: value.id,
                            title: value.title
                        }
                    }">跳转</router-link>
                    */
                
            // 2.接收参数
                // this.$route.query.id
                // this.$route.query.title


        // 7.路由的命名
            // 1.作用：可以简化路由的跳转
            // 2.如何使用：
                // 1.给路由命名
                    /*
                    {
                        path: '/demo',
                        component: Demo,
                        children: [
                            {
                                path: 'test',
                                component: 'Test',
                                children: [
                                    {
                                        name: 'hello'   //给路由命名
                                        path: 'welcome',
                                        component: Hello
                                    }
                                ]
                            }
                        ]
                    }
                    */

            // 3.简化跳转
                // 简化前，需要写完整的路径
                    // <router-link to='/demo/test/welcome'>跳转</router-link>
                // 简化后，直接通过名字跳转
                    // <router-link :to='{name: "hell0"}'>跳转</router-link>
                // 简化后配合传递参数
                    /*
                    <router-link :to='{
                        name: "hello",
                        query: {
                            id: "001",
                            title: "你好"
                        }
                    }'>跳转</router-link>
                    */


        // 8.路由的params参数
            // 1.配置路由的时候一定要声明接收params参数
                routers: [
                    {
                        path: '/About',
                        component: About,
                        children: [
                            {
                                path: 'About_1',
                                component: About_1,
                                children: [
                                    {
                                        name: 'detail',
                                        path: 'detail/:id/:title', //使用占位符接收params参数
                                        component: Detail
                                    }
                                ]
                            }
                        ]
                    }
                ]
            // 2.接收参数
                // 跳转并携带params参数，to的字符串写法
                    // <router-link :to='`/About/About_1/detail/${value.id}/${value.title}`'>跳转</router-link>
                // 跳转并携带params参数，to的对象写法
                    /*
                    <router-link :to='{
                        name: "detail",
                        params: {
                            id: value.id,
                            title: value.title
                        }
        
                    }'>跳转</router-link>
                    */
                // 特别注意：路由携带params参数时，若使用to的对象写法，则不能使用path配置项，必须使用name配置!

            // 3.接收参数
                // this.$route.params.id
                // this.$route.params.title




        // 9.路由的props配置
            // 1.作用：让路由组件更加方便的收到参数
                routers: [
                    {
                        name: 'guanyu',
                        path: '/About',
                        component: About,
                        // 第一种写法：props值为对象，该对象中所有的key-value的组合最终都会通过props传给About组件
                        // props: {id: '001', title: 'title'}

                        // 第二种写法：props值为布尔值，布尔值为true，则把路由收到的所有params参数通过props传给About组件
                        // props: true

                        // 第三种写法(推荐使用这种)：props值为函数，该函数返回一个对象中每一组key-value都会通过props传给About组件
                        props($route) {
                            return {
                                id: $route.query.id,
                                title: $route.query.title,
                                age: $route.query.age
                            }
                        }
                    }
                ]

            // 2.接收方法：在接收数据的组件中配置
                export default {
                    name: 'About',
                    props: ['id', 'title', 'age']
                }




        // 10.<router-link>的replace属性
            // 1.作用：控制路由跳转时操作浏览器历史记录的模式
            // 2.浏览器的历史记录有两种写入方式：分别为 push 和 replace，push是追加历史记录，repace是替换当前记录。路由跳转时候默认为push
            // 3.如何开启replace模式：<router-link replace>News</router-link>

            



        // 11.编程式路由导航
            // 1.作用：不借助<router-link>实现路由跳转，让路由跳转更加灵活
            // 2.具体编码：
                // $router的2个api
                // push方法
                this.$router.push({
                    name: 'About',
                    params: {
                        id: xxx,
                        title: xxx
                    }
                })

                // replace方法
                this.$router.replace({
                    name: 'about',
                    params: {
                        id: xxx,
                        title: xxx
                    }
                })
            
            // 3.$router的前进、后退、指定后退或前进的步数
                // 后退一步
                    this.$router.back()
                // 前进一步
                    this.$router.forward();
                // 如果传入负数表示后退，传入正数表示前进,当前表示后退两步
                    this.$router.go(-2)



        // 12.缓存路由组件
            // 1.作用：让不展示的路由组件保持挂在，不销毁
            // 2.具体编码：
                // <keep-alive include="about"> 
                //     <router-view></router-view>
                // </keep-alive>
                
                // 注意：include='指定需要缓存的组件' 如果不写include则默认缓存路由视图下的所有，缓存多个 :include="['about', 'home']"




        // 13.两个新的生命周期钩子
            // 1.作用：路由组件独有的两个生命周期钩子，用于捕获路由组件的激活状态。
            // 2.具体名字：
                /*
                activated() {
                    // 路由组件被激活时调用
                },
                deactivated() {
                    // 路由组件被失活时调用
                }
                */
            
        




        // 14.路由守卫(权限)
            // 1.作用：对路由权限的控制
            // 2.分类：全局守卫、独享守卫、组件内守卫
            // 3.全局守卫：
                // 全局前置路由守卫——————初始化的时候被调用、每次路由切换之前被调用
                    router.beforeEach((to, from, next) => {
                        // to: 去哪
                        // from：你来自与那
                        // next：放行
                        // console.log(to);
                        if(to.meta.isAuth) {  //判断是否需要鉴权
                            if(localStorage.getItem('school') === 'HYLG') {
                                next();
                            }else {
                                alert('权限不足')
                            }
                        }else {
                            next();
                        }
                    });

                // 全局后置路由守卫——————初始化的时候被调用、每次路由切换之后被调用(后置路由没有next方法)
                    router.afterEach((to, from) => {
                        document.title = to.meta.title || "首页"
                    })
            

            // 4.独享路由守卫(独享路由守卫之后前置没有后置)
                routers: [
                    {
                        // 在进入之前调用
                        beforeEnter(to, from, next) {
                            if(localStorage.getItem('school') === 'HYLG') {
                                next();
                            }else {
                                alert('学校名称错误，无权限查看')
                            }
                        },
                        path: '/GuangDong',
                        component: GuangDong,
                        meta: {  //meta是一个对象里面的属性可以自定义
                            title: '广东',
                            isAuth: true
                        }
                    }
                ]
                
            
            

            // 5.组件内路由守卫(在组件里面配置)
                export default {
                    name: "GD",
                    // 通过路由规则，进入该组件时被调用
                    beforeRouteEnter (to, from, next) {
                        // ...
                        console.log(to.meta.isAuth);
                        if(to.meta.isAuth) { //判断是否需要鉴权
                            if(localStorage.getItem('school') === 'HYLG') {
                                next();
                            }
                        }else {
                            alert('当前权限不足无法访问')
                        }
                    },
                    // 通过路由规则，离开该组件时被调用
                    beforeRouteLeave (to, from, next) {
                        // ...
                        console.log(to.path);
                        if(to.path == '/GuangXi') {
                            next();
                        }else {
                            alert('当前页面只能切换到GuangXi')
                        }
                    }
                }








        // 15. 路由器的两种工作模式(history and hash)
            // 1.对于一个url来说，什么是hash指?————#及其后面的内容就是hash值。
            // 2.hash值不会包含在HTTP请求中，即：hash值不会带给服务器。
            // 3.hash模式：
                // 1) 地址中永远带着#号,不美观
                // 2) 若以后将地址通过第三方手机app分享，若app校验严格，则地址会被标记不合法。
                // 3) 兼容性好
            // 4.history模式：
                // 1.地址干净，美观
                // 2.兼容性和hash模式相比略差。
                // 3.应用部署上线时需要后端人员支持，解决刷新页面服务器404的问题。
            











    // 43.element-ui基本使用